﻿using System;
using System.Xml;
using Line20.DateTimeRangeField.ViewModels;
using Line20.DateTimeRangeField.Settings;
using Line20.DateTimeRangeField;
using Orchard;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Localization;
using Orchard.ContentManagement.Handlers;

namespace Line20.DateTimeRangeField.Drivers {
    public class DateTimeRangeFieldDriver : ContentFieldDriver<Fields.DateTimeRangeField> {
        public IOrchardServices Services { get; set; }
        private const string TemplateName = "Fields/Line20.DateTimeRange"; // EditorTemplates/Fields/Line20.DateTimeRange.cshtml

        public DateTimeRangeFieldDriver(IOrchardServices services) {
            Services = services;
            T = NullLocalizer.Instance;
        }

        public Localizer T { get; set; }

        private static string GetPrefix(ContentField field, ContentPart part) {
            return part.PartDefinition.Name + "." + field.Name;
        }

        private static string GetDifferentiator(Fields.DateTimeRangeField field, ContentPart part) {
            return field.Name;
        }

        /// <summary>
        /// Gets called when the field is displayed 
        /// </summary>
        protected override DriverResult Display(ContentPart part, Fields.DateTimeRangeField field, string displayType, dynamic shapeHelper) {
            var settings = field.PartFieldDefinition.Settings.GetModel<DateTimeRangeFieldSettings>();

            var viewModel = new DateTimeRangeFieldViewModel {
                DisplayName = field.Name,
                StartDateTime = GetLocalDateTimeString(field.StartDateTime, settings.Display),
                EndDateTime = GetLocalDateTimeString(field.EndDateTime, settings.Display, true),
                Display = settings.Display,
                Culture = settings.Culture 
            };


            return ContentShape("Fields_Line20_DateTimeRange", GetDifferentiator(field, part),
                () => shapeHelper.Fields_Line20_DateTimeRange(ContentPart: part, ContentField: field, Model: viewModel));
        }

        /// <summary>
        /// Gets called when the field is used in edit mode (GET)
        /// </summary>
        protected override DriverResult Editor(ContentPart part, Fields.DateTimeRangeField field, dynamic shapeHelper) {
            var settings = field.PartFieldDefinition.Settings.GetModel<DateTimeRangeFieldSettings>();

            var viewModel = new DateTimeRangeFieldViewModel {
                DisplayName = field.DisplayName,
                StartDateTime = GetLocalDateTimeString(field.StartDateTime, settings.Display),
                EndDateTime = GetLocalDateTimeString(field.EndDateTime, settings.Display, true),
                Hint = settings.Hint,
                Required = settings.Required,
                Display = settings.Display,
                Culture = settings.Culture
            };

            return ContentShape("Fields_Line20_DateTimeRange_Edit", GetDifferentiator(field, part),
                () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: viewModel, Prefix: GetPrefix(field, part)));
        }

        /// <summary>
        /// Gets called when the field is used in edit mode (POST)
        /// </summary>
        protected override DriverResult Editor(ContentPart part, Fields.DateTimeRangeField field, IUpdateModel updater, dynamic shapeHelper) {
            var viewModel = new DateTimeRangeFieldViewModel();

            if (updater.TryUpdateModel(viewModel, GetPrefix(field, part), null, null)) {
                // Default time to "00:00" when chosen setting is DateOnly
                var settings = field.PartFieldDefinition.Settings.GetModel<DateTimeRangeFieldSettings>();
                if (settings.Display == DateTimeRangeFieldDisplays.DateOnly) {
                    viewModel.StartDateTime += " " + DateTime.MinValue.ToShortTimeString();
                    viewModel.EndDateTime += " " + DateTime.MinValue.ToShortTimeString();
                }

                // Default date to "01/01/0001" when chosen setting is TimeOnly
                if (settings.Display == DateTimeRangeFieldDisplays.TimeOnly) {
                    viewModel.StartDateTime = DateTime.MinValue.ToShortDateString() + " " + viewModel.StartDateTime;
                    viewModel.EndDateTime = DateTime.MinValue.ToShortDateString() + " " + viewModel.EndDateTime;
                }

                field.StartDateTime = ConvertToUTCDateTime(viewModel.StartDateTime);
                if (!field.StartDateTime.HasValue)
                    updater.AddModelError(GetPrefix(field, part), T("{0} has an invalid start date and/or time.", field.DisplayName));

                // Set End date equal to Start date when chosen setting is SingleDateTime
                if (settings.Display == DateTimeRangeFieldDisplays.SingleDateAndTime) {
                    viewModel.EndDateTime = field.StartDateTime.Value.ToShortDateString() + " " + viewModel.EndDateTime;
                }
                
                field.EndDateTime = ConvertToUTCDateTime(viewModel.EndDateTime);
                if (!field.EndDateTime.HasValue)
                    updater.AddModelError(GetPrefix(field, part), T("{0} has an invalid end date and/or time.", field.DisplayName));

                // Check if End is equal or later than Start
                if (field.StartDateTime != null && field.EndDateTime != null)
                    if (field.EndDateTime < field.StartDateTime)
                        updater.AddModelError(GetPrefix(field, part), T("{0} end must be equal or later than the start.", field.DisplayName));

                if (settings.Required && (field.StartDateTime == DateTime.MinValue || !field.StartDateTime.HasValue)) {
                    updater.AddModelError(field.Name, T("The field {0} is mandatory", T(field.DisplayName)));
                }

            }

            return Editor(part, field, shapeHelper);
        }

        private static string GetLocalDateTimeString(DateTime? dateTime, DateTimeRangeFieldDisplays display, bool isEndDate = false) {
            if (!dateTime.HasValue || dateTime.Value == DateTime.MinValue)
                return string.Empty;

            DateTime localDateTime = dateTime.Value.ToLocalTime();

            if (display == DateTimeRangeFieldDisplays.DateAndTime)
                return localDateTime.ToShortDateString() + " " + localDateTime.ToShortTimeString();
            
            if (display == DateTimeRangeFieldDisplays.DateOnly)
                return localDateTime.ToShortDateString();

            if (display == DateTimeRangeFieldDisplays.TimeOnly)
                return localDateTime.ToShortTimeString();

            if (display == DateTimeRangeFieldDisplays.SingleDateAndTime)
                return isEndDate ? localDateTime.ToShortTimeString() : localDateTime.ToShortDateString() + " " + localDateTime.ToShortTimeString();

            return String.Empty;
        }

       
        private static DateTime? ConvertToUTCDateTime(string dateTime) {
            DateTime value;

            if (!string.IsNullOrWhiteSpace(dateTime)) {
                if (DateTime.TryParse(dateTime, out value))
                    return value.ToUniversalTime();
            }

            return null;
        }

        protected override void Importing(ContentPart part, Fields.DateTimeRangeField field, ImportContentContext context) {
            var importedText = context.Attribute(GetPrefix(field, part), "StartDateTime");
            if (importedText != null) {
                field.Storage.Set("StartDateTime", XmlConvert.ToDateTime(importedText, XmlDateTimeSerializationMode.Utc));
            }

            importedText = context.Attribute(GetPrefix(field, part), "EndDateTime");
            if (importedText != null)
            {
                field.Storage.Set("EndDateTime", XmlConvert.ToDateTime(importedText, XmlDateTimeSerializationMode.Utc));
            }
        }

        protected override void Exporting(ContentPart part, Fields.DateTimeRangeField field, ExportContentContext context) {
            context.Element(GetPrefix(field, part)).SetAttributeValue("StartDateTime", XmlConvert.ToString(field.Storage.Get<DateTime>("StartDateTime"), XmlDateTimeSerializationMode.Utc));
            context.Element(GetPrefix(field, part)).SetAttributeValue("EndDateTime", XmlConvert.ToString(field.Storage.Get<DateTime>("EndDateTime"), XmlDateTimeSerializationMode.Utc));
        }

        protected override void Describe(DescribeMembersContext context) {
            context.Member(null, typeof(string), T("Value"), T("The selected values of the field."));
        }

    }
}